package org.keyroy.mapEditor.ui.views;

import java.io.File;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.widgets.Composite;
import org.keyroy.map.core.manages.Resource;
import org.keyroy.map.core.manages.ResourceManage;
import org.keyroy.map.core.sprite.Sprite;
import org.keyroy.mapEditor.ui.beans.MapUtil;

public class ResourceView extends Composite {
	private Image image;
	private float scale;

	private Resource resource = new Resource();
	private Rectangle selectedArea = new Rectangle(-1, -1, -1, -1);
	private boolean[][] unSelected;
	private boolean singleSelect;

	private Point originPoint = new Point(-1, -1);
	private OnSpritesSelectedListener listener;

	public ResourceView(Composite parent) {
		super(parent, SWT.DOUBLE_BUFFERED);
		resource.singleImage();
		addPaintListener(new PaintListener() {
			@Override
			public void paintControl(PaintEvent e) {
				if (image != null) {
					Point size = getSize();
					org.eclipse.swt.graphics.Rectangle rect = image.getBounds();
					int tx = (size.x - (int) (rect.width * scale)) / 2;
					int ty = (size.y - (int) (rect.height * scale)) / 2;

					Transform transform = new Transform(null);
					transform.translate(tx, ty);
					transform.scale(scale, scale);
					e.gc.setTransform(transform);
					e.gc.drawImage(image, 0, 0);

					if (resource != null) {
						e.gc.setForeground(new Color(getDisplay(), 0x66, 0x99, 0xff));
						e.gc.setLineDash(new int[] { 2, 2 });

						if (resource.splited) {
							for (int x = 0; x < resource.cols; x++) {
								for (int y = 0; y < resource.rows; y++) {
									tx = resource.ox + (resource.paddingLeft + resource.pw) * x;
									ty = resource.oy + (resource.paddingTop + resource.ph) * y;
									e.gc.drawRectangle(tx, ty, resource.pw, resource.ph);
								}
							}
						} else {
							e.gc.drawRectangle(0, 0, rect.width, rect.height);
						}
					}
				}
			}
		});

	}

	public ResourceView(Composite parent, final Resource resource, OnSpritesSelectedListener listener) {
		this(parent, resource, listener, false);
	}

	public ResourceView(Composite parent, final Resource resource, OnSpritesSelectedListener listener,
			boolean singleSelect) {
		this(parent);
		this.singleSelect = singleSelect;
		this.listener = listener;
		this.resource = resource;
		this.unSelected = new boolean[resource.cols][resource.rows];

		setFile(new File(resource.filePath), false);

		addPaintListener(new SelectedAreaPainter());
		MouseActionListener actionListener = new MouseActionListener();
		addMouseListener(actionListener);
		addMouseMoveListener(actionListener);
		addKeyListener(actionListener);
	}

	public void setFile(File file) {
		setFile(file, true);
	}

	public void setFile(File file, boolean isScale) {
		try {
			resource.filePath = file.getAbsolutePath();
			scale = 1.0f;

			ImageData imageData = new ImageData(file.getAbsolutePath());

			image = new Image(null, imageData);
			resource.obj = image;

			Point size = getSize();
			org.eclipse.swt.graphics.Rectangle srcSize = image.getBounds();
			if (isScale == false) {
				setSize(srcSize.width, srcSize.height);
			} else if (srcSize.width > size.x || srcSize.height > size.y) {
				float scaleWidth = (float) size.x / (float) srcSize.width;
				float scaleHeight = (float) size.y / (float) srcSize.height;
				scale = Math.min(scaleWidth, scaleHeight);
			}
			redraw();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public final org.eclipse.swt.graphics.Rectangle getImageSize() {
		try {
			return image.getBounds();
		} catch (Exception e) {
		}

		return new org.eclipse.swt.graphics.Rectangle(0, 0, 0, 0);
	}

	public final Resource getResourceSet() {
		return resource;
	}

	@Override
	protected void checkSubclass() {
	}

	private class SelectedAreaPainter implements PaintListener {

		@Override
		public void paintControl(PaintEvent e) {
			if (hasSelectedArea()) {
				e.gc.setBackground(new Color(getDisplay(), new RGB(0x00, 0x00, 0x00)));
				e.gc.setAlpha(128);
				for (int x = selectedArea.x; x <= selectedArea.x + selectedArea.width; x++) {
					for (int y = selectedArea.y; y <= selectedArea.y + selectedArea.height; y++) {
						if (unSelected[x][y] == false) {
							int tx = resource.ox + (resource.paddingLeft + resource.pw) * x;
							int ty = resource.oy + (resource.paddingTop + resource.ph) * y;
							e.gc.fillRectangle(tx, ty, resource.pw, resource.ph);
						}

					}
				}
				e.gc.setAlpha(255);
			}

		}

	}

	private class MouseActionListener implements MouseListener, MouseMoveListener, org.eclipse.swt.events.KeyListener {
		private boolean isAlowSelectedArea;
		private boolean isCtrlDown;

		@Override
		public void mouseMove(MouseEvent e) {
			if (isLeftPressed(e) && singleSelect == false) {
				Point point = getPoint(e.x, e.y);
				if (point != null) {
					selectedArea.x = originPoint.x;
					selectedArea.y = originPoint.y;
					selectedArea.width = point.x - originPoint.x;
					selectedArea.height = point.y - originPoint.y;
					MapUtil.reset(selectedArea);

					for (int x = selectedArea.x; x <= selectedArea.x + selectedArea.width; x++) {
						for (int y = 0; y <= selectedArea.y + selectedArea.height; y++) {
							unSelected[x][y] = false;
						}
					}

					redraw();
				}
			}

		}

		@Override
		public void mouseDown(MouseEvent e) {
			if (isLeftPressed(e)) {
				Point point = getPoint(e.x, e.y);
				if (checkSelected(point)) {
					callListener();
				} else {
					originPoint = point;
					if (originPoint != null) {
						selectedArea.x = originPoint.x;
						selectedArea.y = originPoint.y;
						selectedArea.width = 0;
						selectedArea.height = 0;
						isAlowSelectedArea = true;
					} else {
						clean();
						isAlowSelectedArea = false;
					}
				}

			} else if (isRightPressed(e)) {
				clean();
				isAlowSelectedArea = false;
			}
			redraw();
		}

		@Override
		public void mouseUp(MouseEvent e) {
			if (isLeftPressed(e)) {
				if (isAlowSelectedArea) {
					callListener();
				}
				isAlowSelectedArea = false;
			}
			redraw();
		}

		private boolean isLeftPressed(MouseEvent e) {
			return e.button == 1 || (e.stateMask & SWT.BUTTON1) != 0;
		}

		private boolean isRightPressed(MouseEvent e) {
			return e.button == 3;
		}

		public Point getPoint(int tx, int ty) {
			Rectangle rectangle = new Rectangle(0, 0, resource.pw, resource.ph);
			for (int x = 0; x < resource.cols; x++) {
				for (int y = 0; y < resource.rows; y++) {
					rectangle.x = resource.ox + (resource.paddingLeft + resource.pw) * x;
					rectangle.y = resource.oy + (resource.paddingTop + resource.ph) * y;
					if (contains(rectangle, tx, ty)) {
						return new Point(x, y);
					}
				}
			}
			return null;
		}

		private void clean() {
			selectedArea.x = -1;
			selectedArea.y = -1;
			selectedArea.width = 0;
			selectedArea.height = 0;
		}

		@Override
		public void mouseDoubleClick(MouseEvent e) {
		}

		@Override
		public void keyPressed(KeyEvent e) {
			isCtrlDown = e.keyCode == SWT.CTRL;
		}

		@Override
		public void keyReleased(KeyEvent e) {
			isCtrlDown = false;
		}

		private boolean checkSelected(Point point) {
			if (point != null && isCtrlDown && contains(selectedArea, point.x, point.y)) {
				unSelected[point.x][point.y] = !unSelected[point.x][point.y];
				return true;
			}
			return false;
		}

		private void callListener() {
			if (selectedArea != null && selectedArea.x >= 0 || selectedArea.y >= 0 || selectedArea.width >= 0
					|| selectedArea.height >= 0) {
				Sprite[][] sprites = new Sprite[selectedArea.width + 1][selectedArea.height + 1];
				for (int x = 0; x < sprites.length; x++) {
					for (int y = 0; y < sprites[x].length; y++) {
						if (unSelected[selectedArea.x + x][selectedArea.y + y] == false) {
							Sprite sprite = ResourceManage.getSprite(resource, selectedArea.x + x, selectedArea.y + y);
							sprites[x][y] = sprite;
						}
					}
				}
				listener.onSpriteSelected(resource, sprites);
			}
		}
	}

	private final boolean contains(Rectangle rectangle, int x, int y) {
		return (x >= rectangle.x) && (y >= rectangle.y) && x <= (rectangle.x + rectangle.width)
				&& y <= (rectangle.y + rectangle.height);
	}

	private boolean hasSelectedArea() {
		return selectedArea.x >= 0 && selectedArea.y >= 0;
	}

	public interface OnSpritesSelectedListener {
		public void onSpriteSelected(Resource resources, Sprite[][] sprites);
	}
}
